/**
 * Copyright (c) 2009, Robert Schuster
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or
 * without modification, are permitted provided that the
 * following conditions are met:
 *
 * - Redistributions of source code must retain the above
 *   copyright notice, this list of conditions and the
 *   following disclaimer.
 * - Redistributions in binary form must reproduce the above
 *   copyright notice, this list of conditions and the
 *   following disclaimer in the documentation and/or other
 *   materials provided with the distribution.
 * - Neither the name of Robert Schuster nor the names of his
 *   contributors may be used to endorse or promote products
 *   derived from this software without specific prior written
 *   permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
 * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

#include <JCLcalc.h>
#include <CLcalc.h>
#include <iostream>

#include "Error.hpp"
#include "Executor.hpp"

using namespace std;

/*
 * Class objects to compare to data objects with instanceof
 * init in new Executor
 */
jclass ClassByteArray;
jclass ClassBooleanArray;
jclass ClassCharArray;
jclass ClassShortArray;
jclass ClassIntegerArray;
jclass ClassLongArray;
jclass ClassFloatArray;
jclass ClassDoubleArray;

jclass ClassObjectArray;


/*
 * This Function is not part of the API. It is only internaly used to convert
 * a C++ Error into a Java-Error
 */
jthrowable CErrorToJError(JNIEnv *jenv, Error* err) {
    //is there a Error?
    if (err == NULL) err = new Error(-1);

    //create a new Error-Object
    jclass error_cls = jenv->FindClass("jclcalc/Error");
    jmethodID error_cls_id = jenv->GetMethodID(error_cls, "<init>", "(I)V");

    //set the error id
    jobject error = jenv->NewObject(error_cls, error_cls_id, err->err_id);

    //copy the error string
    jfieldID textid = jenv->GetFieldID(error_cls, "Text", "Ljava/lang/String;");
    jstring error_text = jenv->NewStringUTF(err->Text.c_str());
    jenv->SetObjectField(error, textid, error_text);

    return (jthrowable)error;
}

//find the size of the datatype
size_t getDatatypSizeAndName(JNIEnv * jenv, jobject arg1, string &templatetype) {
    size_t datasize = -1;
    if (jenv->IsInstanceOf(arg1,ClassBooleanArray)) {
        datasize = 1;
        templatetype = "bool";
    } else if (jenv->IsInstanceOf(arg1,ClassByteArray)) {
        datasize = 1;
        templatetype = "char";
    } else if (jenv->IsInstanceOf(arg1,ClassCharArray)) {
        datasize = 2;
        templatetype = "short";
    } else if (jenv->IsInstanceOf(arg1,ClassShortArray)) {
        datasize = 2;
        templatetype = "short";
    } else if (jenv->IsInstanceOf(arg1,ClassIntegerArray)) {
        datasize = 4;
        templatetype = "int";
    } else if (jenv->IsInstanceOf(arg1,ClassLongArray)) {
        datasize = 8;
        templatetype = "long";
    } else if (jenv->IsInstanceOf(arg1,ClassFloatArray)) {
        datasize = 4;
        templatetype = "float";
    } else if (jenv->IsInstanceOf(arg1,ClassDoubleArray)) {
        datasize = 8;
        templatetype = "double";
    }
    return datasize;
}

/*
 * Class:     jclcalc_JCLcalc
 * Method:    CLcalc_init
 * Signature: ()I
 */
JNIEXPORT jint JNICALL Java_jclcalc_JCLcalc_CLcalc_1init(JNIEnv * jenv, jclass cls) {

    //find types of arrays for later comparing
    ClassByteArray = (jclass)(jenv->NewGlobalRef(jenv->FindClass("[B")));
    ClassBooleanArray = (jclass)(jenv->NewGlobalRef(jenv->FindClass("[Z")));
    ClassCharArray = (jclass)(jenv->NewGlobalRef(jenv->FindClass("[C")));
    ClassShortArray = (jclass)(jenv->NewGlobalRef(jenv->FindClass("[S")));
    ClassIntegerArray = (jclass)(jenv->NewGlobalRef(jenv->FindClass("[I")));
    ClassLongArray = (jclass)(jenv->NewGlobalRef(jenv->FindClass("[J")));
    ClassFloatArray = (jclass)(jenv->NewGlobalRef(jenv->FindClass("[F")));
    ClassDoubleArray = (jclass)(jenv->NewGlobalRef(jenv->FindClass("[D")));

    //classtypes for object arrays
    ClassObjectArray = (jclass)(jenv->NewGlobalRef(jenv->FindClass("[Ljava/lang/Object;")));

    //Init the c-Library
    return CLcalc_init();
}

/*
 * Class:     jclcalc_JCLcalc
 * Method:    CLcalc_shutdown
 * Signature: ()I
 */
JNIEXPORT jint JNICALL Java_jclcalc_JCLcalc_CLcalc_1shutdown
  (JNIEnv * env, jclass cls) {
    jint result = CLcalc_shutdown();
    return result;
}

/*
 * Class:     jclcalc_JCLcalc
 * Method:    CLcalc_getDeviceName
 * Signature: (I)Ljava/lang/String;
 */
JNIEXPORT jstring JNICALL Java_jclcalc_JCLcalc_CLcalc_1getDeviceName
  (JNIEnv * jenv, jclass cls, jint device_typ) {

    //get the name of the device as const char*
    const char* cname = CLcalc_getDeviceName(device_typ);

    //create a jstring-object
    jstring jname = jenv->NewStringUTF(cname);

    //return the name
    return jname;
}

/*
 * Class:     jclcalc_JCLcalc
 * Method:    CLcalc_getDeviceSpeed
 * Signature: (I)[I
 */
JNIEXPORT jintArray JNICALL Java_jclcalc_JCLcalc_CLcalc_1getDeviceSpeed
  (JNIEnv * jenv, jclass cls, jint device_typ) {
    //get the speed
    int* speed = CLcalc_getDeviceSpeed(device_typ);

    //create the java array
    jintArray result = jenv->NewIntArray(3);
    jenv->SetIntArrayRegion(result, 0, 3, speed);
    return result;
}

/*
 * Class:     jclcalc_JCLcalc
 * Method:    CLcalc_Executor_new
 * Signature: (I)I
 */
JNIEXPORT jint JNICALL Java_jclcalc_JCLcalc_CLcalc_1Executor_1new
  (JNIEnv * jenv, jclass cls, jint device_typ) {


    //create a new executor and return the id
    return CLcalc_Executor_new(device_typ);
}

/*
 * Class:     jclcalc_JCLcalc
 * Method:    CLcalc_Executor_addProgFromSource
 * Signature: (ILjava/lang/String;Ljava/lang/String;)I
 */
JNIEXPORT jint JNICALL Java_jclcalc_JCLcalc_CLcalc_1Executor_1addProgFromSource
  (JNIEnv * jenv, jclass cls, jint ExeID, jstring source, jstring name) {

    //copy the source and the kernel name
    const char* csource = jenv->GetStringUTFChars(source, NULL);
    const char* cname = jenv->GetStringUTFChars(name, NULL);

    int progid = CLcalc_Executor_addProgFromSource(ExeID, csource, cname);

    //release the Strings
    jenv->ReleaseStringUTFChars(source, csource);
    jenv->ReleaseStringUTFChars(name, cname);

    //any errors?
    if (progid < 0) {
        jenv->Throw(CErrorToJError(jenv, (Error*)CLcalc_getLastErrorObject()));
    }

    //return the program id
    return progid;
}

/*
 * Class:     jclcalc_JCLcalc
 * Method:    CLcalc_Executor_addData
 * Signature: (ILjava/lang/Object;I)I
 */
JNIEXPORT jint JNICALL Java_jclcalc_JCLcalc_CLcalc_1Executor_1addData
  (JNIEnv *jenv, jclass cls, jint ExecutorID, jobject arg1, jlong arg1_dir) {

    //is the array multidimensional?
    int dim = 1;
    int dimsize[10];
    dimsize[0] = jenv->GetArrayLength((jarray)arg1);
    jobject arg_temp = arg1;
    while (jenv->IsInstanceOf(arg_temp,ClassObjectArray)) {
        //get the size of this dimension
        arg_temp = jenv->GetObjectArrayElement((jobjectArray)arg_temp,0);
        dimsize[dim] = jenv->GetArrayLength((jarray)arg_temp);
        dim++;
    }
    for (int i=0;i<dim;i++) cout << dimsize[i] << endl;

    //find the size of the datatype
    long datasize = -1;
    if (jenv->IsInstanceOf(arg_temp,ClassBooleanArray)) {
        datasize = 1;
    } else if (jenv->IsInstanceOf(arg_temp,ClassByteArray)) {
        datasize = 1;
    } else if (jenv->IsInstanceOf(arg_temp,ClassCharArray)) {
        datasize = 2;
    } else if (jenv->IsInstanceOf(arg_temp,ClassShortArray)) {
        datasize = 2;
    } else if (jenv->IsInstanceOf(arg_temp,ClassIntegerArray)) {
        datasize = 4;
    } else if (jenv->IsInstanceOf(arg_temp,ClassLongArray)) {
        datasize = 8;
    } else if (jenv->IsInstanceOf(arg_temp,ClassFloatArray)) {
        datasize = 4;
    } else if (jenv->IsInstanceOf(arg_temp,ClassDoubleArray)) {
        datasize = 8;
    }

    //not found? unsupported data type!
    if (datasize == -1) {
        Error err(-1);
        err.Text = "unsupported data type";
        jenv->Throw(CErrorToJError(jenv, &err));
    }

    //get a pointer to the argument1 array
    void* carg1 = jenv->GetPrimitiveArrayCritical((jarray)arg1, NULL);

    //get the length of argument 1
    int carg1_len = jenv->GetArrayLength((jarray)arg1);

    //Execute the kernel
    int result = CLcalc_Executor_addData(ExecutorID, carg1, datasize*carg1_len, arg1_dir);

    //any errors?
    if (result < 0) {
        jenv->Throw(CErrorToJError(jenv, (Error*)CLcalc_getLastErrorObject()));
    }

    //release the javaarray if we don't need it any longer
    if ((arg1_dir != CL_MEM_USE_HOST_PTR) && (arg1_dir != CL_MEM_COPY_HOST_PTR)) {
        jenv->ReleasePrimitiveArrayCritical((jarray)arg1, carg1, 0);
    }

    //return the dataID
    return result;
}

/*
 * Class:     jclcalc_JCLcalc
 * Method:    CLcalc_Executor_getDataFloat1D
 * Signature: (III[F)V
 */
JNIEXPORT void JNICALL Java_jclcalc_JCLcalc_CLcalc_1Executor_1getData
  (JNIEnv *jenv, jclass cls, jint ExecutorID, jint DataID, jobject data) {

    //get a pointer to the data array
    void* cdata = jenv->GetPrimitiveArrayCritical((jarray)data, NULL);

    //get the length of data
    int cdata_len = jenv->GetArrayLength((jarray)data);

    //read the Data
    int result = CLcalc_Executor_getData(ExecutorID, DataID, cdata, -1);

    //release the pointer
    jenv->ReleasePrimitiveArrayCritical((jarray)data, cdata, 0);

    //errors?
    if (result < 0) {
        jenv->Throw(CErrorToJError(jenv, (Error*)CLcalc_getLastErrorObject()));
    }
}


/*
 * Class:     jclcalc_JCLcalc
 * Method:    CLcalc_Executor_releaseDataFloat1D
 * Signature: (II[F)V
 */
JNIEXPORT void JNICALL Java_jclcalc_JCLcalc_CLcalc_1Executor_1releaseData
  (JNIEnv *jenv, jclass cls, jint ExecutorID, jint DataID, jobject data) {

    //what kind of data object?
    long dir = CLcalc_Executor_getDataDir(ExecutorID, DataID);

    //errors?
    if (dir < 0) {
        jenv->Throw(CErrorToJError(jenv, (Error*)CLcalc_getLastErrorObject()));
    }

    //Release the Java Array, but only if it is pinned bevor
    if ((dir == CL_MEM_USE_HOST_PTR) || (dir == CL_MEM_COPY_HOST_PTR)) {
        void* ptr = CLcalc_Executor_getDataHostPtr(ExecutorID, DataID);
        jenv->ReleasePrimitiveArrayCritical((jarray)data, ptr, 0);
    }

    //Release the data in the device
    int result = CLcalc_Executor_releaseData(ExecutorID, DataID);

    //errors?
    if (result < 0) {
        jenv->Throw(CErrorToJError(jenv, (Error*)CLcalc_getLastErrorObject()));
    }
}

/*
 * Class:     jclcalc_JCLcalc
 * Method:    CLcalc_Executor_setArgument
 * Signature: (IIII)V
 */
JNIEXPORT void JNICALL Java_jclcalc_JCLcalc_CLcalc_1Executor_1setArgument
  (JNIEnv *jenv, jclass cls, jint ExecutorID, jint ProgID, jint arg_pos, jint DataID) {
    int result = CLcalc_Executor_setArgument(ExecutorID, ProgID, arg_pos, DataID);

    //errors?
    if (result < 0) {
        jenv->Throw(CErrorToJError(jenv, (Error*)CLcalc_getLastErrorObject()));
    }
}

/*
 * Class:     jclcalc_JCLcalc
 * Method:    CLcalc_Executor_Execute
 * Signature: (IIII)V
 */
JNIEXPORT void JNICALL Java_jclcalc_JCLcalc_CLcalc_1Executor_1Execute
  (JNIEnv *jenv, jclass cls, jint ExecutorID, jint ProgID, jint MaxIndex) {
    int result = CLcalc_Executor_Execute(ExecutorID, ProgID, MaxIndex);

    //errors?
    if (result < 0) {
        jenv->Throw(CErrorToJError(jenv, (Error*)CLcalc_getLastErrorObject()));
    }
}

/*
 * Class:     jclcalc_JCLcalc
 * Method:    CLcalc_Executor_shutdown
 * Signature: (I)I
 */
JNIEXPORT jint JNICALL Java_jclcalc_JCLcalc_CLcalc_1Executor_1shutdown
  (JNIEnv *jenv, jclass cls, jint ExecutorID) {
    return CLcalc_Executor_shutdown(ExecutorID);
}

/*
 * Class:     jclcalc_JCLcalc
 * Method:    CLcalc_BuildIn_CLintrinsics1arg
 * Signature: (IILjava/lang/Object;Ljava/lang/Object;Ljava/lang/String;Ljava/lang/String;)V
 */
JNIEXPORT void JNICALL Java_jclcalc_JCLcalc_CLcalc_1Executor_1BuildIn_1CLintrinsics1arg
  (JNIEnv * jenv, jclass cls, jint ExecutorID, jobject arg1, jobject arg2, jstring fname) {

    //get the size and the type of the first argument
    string typename1;
    size_t arg1_size = getDatatypSizeAndName(jenv, arg1, typename1);

    //get the size and the type of the second argument
    string typename2;
    size_t arg2_size = getDatatypSizeAndName(jenv, arg2, typename2);

    //both arguments must be of the same type!
    if (typename1.compare(typename2) != 0) {
        Error err(-1);
        err.Text = "input (" + typename1 +") is not of the same type then output (" + typename2 +")";
        jenv->Throw(CErrorToJError(jenv, &err));
        return;
    }

    //get the size of both arrays
    size_t arg1_len = jenv->GetArrayLength((jarray)arg1);
    size_t arg2_len = jenv->GetArrayLength((jarray)arg2);

    //both arguments must be of the same length!
    if (arg1_len != arg2_len) {
        Error err(-1);
        err.Text = "input is not of the same length then output";
        jenv->Throw(CErrorToJError(jenv, &err));
        return;
    }

    //get a pointer to the arrays
    void* carg1 = jenv->GetPrimitiveArrayCritical((jarray)arg1, NULL);
    void* carg2 = jenv->GetPrimitiveArrayCritical((jarray)arg2, NULL);

    //execute the function
    const char* cfname = jenv->GetStringUTFChars(fname, NULL);
    int length[1];
    length[0] = arg1_len;
    int result = CLcalc_Executor_BuildIn_CLintrinsics1arg(ExecutorID, carg1, carg2, &length[0], 1, cfname, typename1.c_str());
    jenv->ReleaseStringUTFChars(fname, cfname);

    //release the pointer
    jenv->ReleasePrimitiveArrayCritical((jarray)arg1, carg1, 0);
    jenv->ReleasePrimitiveArrayCritical((jarray)arg2, carg2, 0);

    //errors?
    if (result < 0) {
        jenv->Throw(CErrorToJError(jenv, (Error*)CLcalc_getLastErrorObject()));
    }

}

/*
 * Class:     jclcalc_JCLcalc
 * Method:    CLcalc_BuildIn_CLintrinsics1arg_execOnly
 * Signature: (IIIILjava/lang/String;)V
 */
JNIEXPORT void JNICALL Java_jclcalc_JCLcalc_CLcalc_1Executor_1BuildIn_1CLintrinsics1arg_1execOnly
  (JNIEnv *jenv, jclass cls, jint ExecutorID, jint inputID, jobject input, jint outputID, jobject output, jstring fname) {
    //get the size and the type of the first argument
    string typename1;
    size_t arg1_size = getDatatypSizeAndName(jenv, input, typename1);

    //get the size and the type of the second argument
    string typename2;
    size_t arg2_size = getDatatypSizeAndName(jenv, output, typename2);

    //both arguments must be of the same type!
    if (typename1.compare(typename2) != 0) {
        Error err(-1);
        err.Text = "input (" + typename1 +") is not of the same type then output (" + typename2 +")";
        jenv->Throw(CErrorToJError(jenv, &err));
        return;
    }

    //get the size of both arrays
    size_t arg1_len = jenv->GetArrayLength((jarray)input);
    size_t arg2_len = jenv->GetArrayLength((jarray)output);

    //both arguments must be of the same length!
    if (arg1_len != arg2_len) {
        Error err(-1);
        err.Text = "input is not of the same length then output";
        jenv->Throw(CErrorToJError(jenv, &err));
        return;
    }

    //execute the function
    const char* cfname = jenv->GetStringUTFChars(fname, NULL);
    int length[1];
    length[0] = arg1_len;
    int result = CLcalc_Executor_BuildIn_CLintrinsics1arg_execOnly(ExecutorID, inputID, outputID, &length[0], 1, cfname, typename1.c_str());
    jenv->ReleaseStringUTFChars(fname, cfname);

    //errors?
    if (result < 0) {
        jenv->Throw(CErrorToJError(jenv, (Error*)CLcalc_getLastErrorObject()));
    }

}

/*
 * Class:     jclcalc_JCLcalc
 * Method:    CLcalc_BuildIn_basic
 * Signature: (IILjava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/String;)V
 */
JNIEXPORT void JNICALL Java_jclcalc_JCLcalc_CLcalc_1Executor_1BuildIn_1basic
  (JNIEnv *jenv, jclass cls, jint ExecutorID, jobject arg1, jobject arg2, jobject result, jstring fname) {

    //get the size and the type of the first argument
    string typename1;
    size_t arg1_size = getDatatypSizeAndName(jenv, arg1, typename1);

    //get the size and the type of the second argument
    string typename2;
    size_t arg2_size = getDatatypSizeAndName(jenv, arg2, typename2);

    //get the size and the type of the result
    string typenameResult;
    size_t result_size = getDatatypSizeAndName(jenv, result, typenameResult);

    //all arguments must be of the same type!
    if ((typename1.compare(typename2) != 0) || (typenameResult.compare(typename2)))  {
        Error err(-1);
        err.Text = "argument 1 (" + typename1 +"), argument 2 (" + typename1 +") and the result (" + typenameResult +") are not of the same type";
        jenv->Throw(CErrorToJError(jenv, &err));
        return;
    }

    //get the size of all arrays
    size_t arg1_len = jenv->GetArrayLength((jarray)arg1);
    size_t arg2_len = jenv->GetArrayLength((jarray)arg2);
    size_t result_len = jenv->GetArrayLength((jarray)result);

    //both arguments must be of the same length!
    if ((arg1_len != arg2_len) || (arg1_len != result_len)) {
        Error err(-1);
        err.Text = "all arrays must have the same size";
        jenv->Throw(CErrorToJError(jenv, &err));
        return;
    }

    //get a pointer to the arrays
    void* carg1 = jenv->GetPrimitiveArrayCritical((jarray)arg1, NULL);
    void* carg2 = jenv->GetPrimitiveArrayCritical((jarray)arg2, NULL);
    void* cresult = jenv->GetPrimitiveArrayCritical((jarray)result, NULL);

    //execute the function
    const char* cfname = jenv->GetStringUTFChars(fname, NULL);
    int length[1];
    length[0] = arg1_len;
    int err = CLcalc_Executor_BuildIn_basic(ExecutorID, carg1, carg2, cresult, &length[0], 1, cfname, typename1.c_str());
    jenv->ReleaseStringUTFChars(fname, cfname);

    //release the pointer
    jenv->ReleasePrimitiveArrayCritical((jarray)result, cresult, 0);
    jenv->ReleasePrimitiveArrayCritical((jarray)arg2, carg2, 0);
    jenv->ReleasePrimitiveArrayCritical((jarray)arg1, carg1, 0);

    //errors?
    if (err < 0) {
        jenv->Throw(CErrorToJError(jenv, (Error*)CLcalc_getLastErrorObject()));
    }

}

/*
 * Class:     jclcalc_JCLcalc
 * Method:    CLcalc_BuildIn_basic_execOnly
 * Signature: (IIILjava/lang/Object;ILjava/lang/Object;ILjava/lang/Object;Ljava/lang/String;)V
 */
JNIEXPORT void JNICALL Java_jclcalc_JCLcalc_CLcalc_1Executor_1BuildIn_1basic_1execOnly
  (JNIEnv *jenv, jclass cls, jint ExecutorID, jint arg1ID, jobject arg1, jint arg2ID, jobject arg2, jint resultID, jobject result, jstring fname) {

    //get the size and the type of the first argument
    string typename1;
    size_t arg1_size = getDatatypSizeAndName(jenv, arg1, typename1);

    //get the size and the type of the second argument
    string typename2;
    size_t arg2_size = getDatatypSizeAndName(jenv, arg2, typename2);

    //get the size and the type of the result
    string typenameResult;
    size_t result_size = getDatatypSizeAndName(jenv, result, typenameResult);

    //all arguments must be of the same type!
    if ((typename1.compare(typename2) != 0) || (typenameResult.compare(typename2)))  {
        Error err(-1);
        err.Text = "argument 1 (" + typename1 +"), argument 2 (" + typename1 +") and the result (" + typenameResult +") are not of the same type";
        jenv->Throw(CErrorToJError(jenv, &err));
        return;
    }

    //get the size of all arrays
    size_t arg1_len = jenv->GetArrayLength((jarray)arg1);
    size_t arg2_len = jenv->GetArrayLength((jarray)arg2);
    size_t result_len = jenv->GetArrayLength((jarray)result);

    //both arguments must be of the same length!
    if ((arg1_len != arg2_len) || (arg1_len != result_len)) {
        Error err(-1);
        err.Text = "all arrays must have the same size";
        jenv->Throw(CErrorToJError(jenv, &err));
        return;
    }

    //execute the function
    const char* cfname = jenv->GetStringUTFChars(fname, NULL);
    int length[1];
    length[0] = arg1_len;
    int err = CLcalc_Executor_BuildIn_basic_execOnly(ExecutorID, arg1ID, arg2ID, resultID, &length[0], 1, cfname, typename1.c_str());
    jenv->ReleaseStringUTFChars(fname, cfname);

    //errors?
    if (err < 0) {
        jenv->Throw(CErrorToJError(jenv, (Error*)CLcalc_getLastErrorObject()));
    }
}




